const { Int64BE } = require('int64-buffer')
const strings = require('pako/lib/utils/strings')

var VINT_NB = 0x80
var VINT_B = VINT_NB - 1
var VINT = VINT_B
var VINT_1_MAX = VINT
var VINT_2_MAX = VINT_1_MAX + (VINT << 7)
var VINT_3_MAX = VINT_2_MAX + (VINT << 14)
var VINT_4_MAX = VINT_3_MAX + (0XFF << 21)

function byte(val) {
    return val & 0XFF
}

var WEncoder = false
try {
    WEncoder = typeof (TextEncoder) === 'function' && TextEncoder.prototype.encode

} catch (e) {
    console.error(e)
}

module.exports = {

    byte: byte,

    empty: new Uint8Array(0),

    Int64BE: Int64BE,
    strings: strings,

    getVIntLen(vInt) {
        if (vInt <= VINT_1_MAX) {
            return 1
        }

        if (vInt <= VINT_2_MAX) {
            return 2
        }

        if (vInt <= VINT_3_MAX) {
            return 3
        }

        return 4
    },

    setVInt(bs, off, val, setOff) {
        if (val > VINT_1_MAX) {
            bs[off] = byte(val) & VINT_B | VINT_NB
            off++
            if (val > VINT_2_MAX) {
                bs[off] = (byte(val >> 7) & VINT_B | VINT_NB)
                off++
                if (val > VINT_3_MAX) {
                    if (val > VINT_4_MAX) {
                        console.error(`vInt err max ${VINT_4_MAX}, ${val}`)

                    } else {
                        bs[off] = byte(val >> 14) & VINT_B | VINT_NB
                        off++
                        bs[off] = byte(val >> 21)
                        off++
                    }

                } else {
                    bs[off] = byte(val >> 14) & VINT_B
                    off++
                }

            } else {
                bs[off] = byte(val >> 7) & VINT_B
                off++
            }

        } else {
            bs[off] = byte(val) & VINT_B
            off++
        }

        if (setOff) {
            setOff(off)
        }
    },

    getVInt(bs, off, setOff) {
        var b = bs[off]
        off++
        var val = byte(b) & VINT
        if ((b & VINT_NB) != 0) {
            b = bs[off]
            off++
            val += byte(b & VINT_B) << 7
            if ((b & VINT_NB) != 0) {
                b = bs[off]
                off++
                val += byte(b & VINT_B) << 14
                if ((b & VINT_NB) != 0) {
                    b = bs[off]
                    off++
                    val += byte(b) << 21
                }
            }
        }

        if (setOff) {
            setOff(off)
        }

        return val
    },

    getInt64(bs, off, setOff) {
        var buff = new Uint8Array(8)
        for (var i = 0; i < 8; i++) {
            buff[i] = byte(bs[off + 7 - i])
        }

        if (setOff) {
            setOff(off + 8)
        }

        return Int64BE(buff).toString(10)
    },

    int64ToBytes(int64) {
        return new Int64BE(int64).toBuffer()
    },

    setBytes(bs, off, bytes, setOff) {
        var len = bytes.length
        for (var i = 0; i < len; i++) {
            bs[off] = bytes[i]
            off++
        }

        if (setOff) {
            setOff(off)
        }
    },

    getBytes(bs, off, len, setOff) {
        var bytes = new Uint8Array(len)
        for (var i = 0; i < len; i++) {
            bytes[i] = bs[off]
            off++
        }

        if (setOff) {
            setOff(off)
        }

        return bytes
    },

    stringToBytes(str) {
        if (!str) {
            return str == "" ? new Uint8Array(0) : null
        }

        if (WEncoder) {
            var encoder = new TextEncoder();
            return encoder.encode(str);

        } else {
            return strings.string2buf(str);
        }
    },

    bytesToString(bytes) {
        if (!bytes) {
            return null
        }

        if (WEncoder) {
            var decoder = new TextDecoder();
            return decoder.decode(bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes));

        } else {
            if (!(bytes instanceof Uint8Array)) {
                bytes = new Uint8Array(bytes)
            }

            return strings.buf2string(bytes)
        }
    },

    // blob读取
    blobLoadResult(blob, back) {
        if (!blob) {
            back(null)
            return
        }

        var reader = new FileReader()
        reader.onload = function (e) {
            back(reader.result)
        }

        reader.readAsArrayBuffer(blob)
    },

    // 解密
    decrypt(bs, keys) {
        var bLen = bs ? bs.length : 0
        var kLen = keys ? keys.length : 0
        if (!(bLen > 0 && kLen > 0)) {
            return bs
        }

        var b = 0
        var k = 0
        var c
        for (var i = bLen - 1; i >= 0; i--) {
            b ^= byte(keys[k] + i)
            c = bs[i] ^ b
            bs[i] = c
            b ^= c
            k++
            if (k >= kLen) {
                k = 0
            }
        }

        b = 0
        k = 0
        for (var i = 0; i < bLen; i++) {
            b ^= keys[k]
            c = bs[i] ^ b
            bs[i] = c
            b ^= c
            k++
            if (k >= kLen) {
                k = 0
            }
        }

        return bs
    },

    // 加密
    encrypt(bs, keys) {
        var bLen = bs ? bs.length : 0
        var kLen = keys ? keys.length : 0
        if (!(bLen > 0 && kLen > 0)) {
            return bs
        }

        var b = 0
        var k = 0
        for (var i = 0; i < bLen; i++) {
            b ^= bs[i] ^ keys[k]
            bs[i] = b
            k++
            if (k >= kLen) {
                k = 0
            }
        }

        b = 0
        k = 0
        for (var i = bLen - 1; i >= 0; i--) {
            b ^= bs[i] ^ byte(keys[k] + i)
            bs[i] = b
            k++
            if (k >= kLen) {
                k = 0
            }
        }

        return bs
    },
}